﻿namespace HuwmanCode.Diagnostics.Logging

open System
open System.Diagnostics
open System.Collections.Generic
open System.ComponentModel.Composition
open HuwmanCode.ComponentModel.Composition.Hosting

[<AutoOpen>]
module Trace =
    /// <summary>
    /// The trace workflow builder
    /// </summary>
    type TraceBuilder(workflowName:string) =
        let logger =
            let exports = 
                DefaultCompositionContainer.getInstance().GetExports(typeof<ILogger>, typeof<IDictionary<string,Object>>, null) 
                |> Seq.map (fun item -> new Lazy<_,_> ((fun _ -> item.Value :?> ILogger), item.Metadata :?> IDictionary<string,Object>))

            let export = //If one export is returned use it, otherwise use the non-default export
                if exports |> Seq.length = 1 then exports |> Seq.nth 0
                else exports |> Seq.skipWhile (fun item -> item.Metadata.TryGetValue ("TypeName") |> fun (_,v) -> v = box "HuwmanCode.Diagnostics.Logging.DefaultLogger") |> Seq.nth 0

            export.Value

        let bind value f =
            f value

        let combine a b =
            a |> bind (fun () -> b)

        let result value =
            fun() -> value

        let tryFinally f compensate =
            try
                f()
            finally
                compensate()

        let using (res:#IDisposable) f =
            let tryBlock () =
                sprintf "Workflow '%s' created instance of '%s'." workflowName (res.GetType().FullName) |> logger.Debug.Write  
                f res
            
            let compensate () =
                res.Dispose()
                sprintf "Workflow '%s' disposed instance of '%s'." workflowName (res.GetType().FullName) |> logger.Debug.Write

            tryFinally tryBlock compensate

        let delay f = 
            try
                let v = f()()
                if v :> Object = null then sprintf "Workflow '%s' completed." workflowName |> logger.Debug.Write  
                else sprintf "Workflow '%s' completed with result '%O'." workflowName v |> logger.Debug.Write      
                v
            with e -> 
                (sprintf "Workflow '%s' terminated with exception '%s'!" workflowName, e) |> logger.Error.Write
                reraise()

        member x.Bind(v, f) = bind v f
        member x.Delay(f) = delay f
        member x.Combine(a,b) = combine a b
        member x.Return(v) = result v
        member x.Zero() = result ()
        member x.TryFinally(f, compensate) = tryFinally f compensate
        member x.Using(res: #IDisposable, f) = using res f  

    let trace workflowName = 
        new TraceBuilder(workflowName)
    